using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using System.Web.UI.WebControls;
using System.IO;
using System.Diagnostics;
using Microsoft.SharePoint.Utilities;
using System.Xml.Xsl;
using Microsoft.SharePoint.WebPartPages;
using System.Text.RegularExpressions;
using System.Text;
using System.Collections;

namespace XSLTListitemWebPart
{
    [Guid("ed3e302f-dd66-420f-910b-1a2254a4651e")]
    [SupportsAttributeMarkup(true), ParseChildren(true)]
    public class XSLTListitemWebPartFeature : Microsoft.SharePoint.WebPartPages.WebPart
    {
        protected string[] _assemblyReferences = new string[] { "<%@ Register TagPrefix=\"SharePoint\" Namespace=\"Microsoft.Sharepoint.WebControls\" Assembly=\"Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c\" %>" };
        private string _partContent;
        private static Regex _runatCheck = null;

        private Regex RunatChecker
        {
            get
            {
                if (_runatCheck == null)
                {
                    _runatCheck = new Regex("runat=\"server\"", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
                }
                return _runatCheck;
            }
        }

        #region Public Properties
        /// <summary>
        /// Property to hold the Url of the ListItem to be displayed
        /// </summary>
        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(false)]
        public string ListItemUrl { get; set; }

        /// <summary>
        /// Property to hold the Xslt TemplateName to use
        /// </summary>
        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(false)]
        public string RenderTemplateName { get; set; }

        /// <summary>
        /// Property to hold the Url of the XSLT used to render
        /// </summary>
        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(false)]
        public string XSLTLink { get; set; }
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        public XSLTListitemWebPartFeature()
        {
            this.ExportMode = WebPartExportMode.All;
        }

        /// <summary>
        /// This method will handle an erroneous situation by creating an visually hidden control with exception-information.
        /// </summary>
        /// <param name="Message">An informational string which describes which process was being done</param>
        /// <param name="ex">The exception which occured</param>
        private void HandleError(string Message, Exception ex)
        {
            // Create a message which can help developers debug
            string FullMessage = this.GetType().Name + ": " + Message + Environment.NewLine + ex.ToString();

            Label lbl = new Label();
            lbl.Style["display"] = "none";
            lbl.Text = FullMessage;
            this.Controls.Add(lbl);

            Trace.WriteLine(FullMessage);

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                EventLog.WriteEntry(this.GetType().FullName, FullMessage, EventLogEntryType.Error);
            });
        }

        /// <summary>
        /// This method will be ran with Elevated Privileges and will add the XSLT transformation output as an literalcontrol
        /// </summary>
        private void CreateOutputControl(SPListItem listitem)
        {
            SPSite currentsite = null;
            SPSite lisite = null;
            SPWeb liweb = null;

            try
            {
                // Create a new elevated-priviliges SPSite
                currentsite = new SPSite(SPContext.Current.Site.ID);

                string Xslt = null;

                #region Retrieve XSLT from document library by Url
                try
                {
                    SPFolder folder = currentsite.RootWeb.GetFolder(XSLTLink);
                    Xslt = folder.ParentWeb.GetFileAsString(XSLTLink);
                }
                catch (Exception ex)
                {
                    HandleError(string.Format("Error when retrieving XSLT from url '{0}'", XSLTLink), ex);
                }
                #endregion

                List<SPListItem> listitems = new List<SPListItem>();
                listitems.Add(listitem);


                #region Transform XSLT
                if (!string.IsNullOrEmpty(Xslt))
                {
                    try
                    {
                        StringWriter output = new StringWriter();
                        XsltArgumentList argumentlist = new XsltArgumentList();
                        /*foreach (WebPartConnection connection in Connections)
                        {
                            // Not yet implemented
                        }*/
                        argumentlist.AddParam("WebPartUniqueID", "", (this.UniqueID!=null?this.UniqueID:string.Empty));
                        argumentlist.AddParam("WebPartClientID", "", (this.ClientID!=null?this.ClientID:string.Empty));
                        argumentlist.AddParam("WebPartStorageKey", "", this.StorageKey);
                        argumentlist.AddParam("WebPartWidth", "", (this.Width!=null?this.Width:string.Empty));
                        argumentlist.AddParam("WebPartHeight", "", (this.Height!=null?this.Height:string.Empty));
                        argumentlist.AddParam("WebPartDisplayTitle", "", (this.DisplayTitle!=null?this.DisplayTitle:string.Empty));
                        argumentlist.AddParam("WebPartSubtitle", "", (this.Subtitle!=null?this.Subtitle:string.Empty));
                        argumentlist.AddParam("WebPartTitle", "", (this.Title!=null?this.Title:string.Empty));
                        argumentlist.AddParam("WebPartZoneID", "", (this.ZoneID!=null?this.ZoneID:string.Empty));
                        argumentlist.AddParam("RenderTemplateName", "", (this.RenderTemplateName!=null?this.RenderTemplateName:string.Empty));
                        XSLTHelper.Transform(Xslt, listitems, argumentlist, output);
                        _partContent = output.ToString();

                        Regex vobjRegex = new Regex("<[^>]*((xmlns(:\\w+)?)|(xsi(:\\w+)?))\\s*=\\s*(\"[^\"]*\")[^>]*>");
                        Regex vobjRegexReplace = new Regex("((xmlns(:\\w+)?)|(xsi(:\\w+)?))\\s*=\\s*(\"[^\"]*\")");
                        Match vobjMatch = vobjRegex.Match(_partContent);
                        while (vobjMatch.Success)
                        {
                            _partContent = _partContent.Replace(vobjMatch.Value, vobjRegexReplace.Replace(vobjMatch.Value, ""));
                            vobjMatch = vobjRegex.Match(_partContent);
                        }

                        
                        //Literal outputcontrol = new Literal();
                        //outputcontrol.Text = output.ToString();
                        //Controls.Add(outputcontrol);
                    }
                    catch (Exception ex)
                    {
                        HandleError(string.Format("Error when doing transformation with XSLT from url '{0}' on ListItem from url '{1}'", XSLTLink, ListItemUrl), ex);
                    }
                }
                #endregion
            }
            catch (Exception ex)
            {
                HandleError("Error", ex);
            }
            finally
            {
                if (currentsite != null) currentsite.Dispose();
                if (liweb != null) liweb.Dispose();
                if (lisite != null) lisite.Dispose();
            }
        }

        /// <summary>
        /// An overridden implementation
        /// </summary>
        protected override void CreateChildControls()
        {
            if (!string.IsNullOrEmpty(XSLTLink) && SPContext.Current != null)
            {
                SPListItem listitem = null;
                if (!string.IsNullOrEmpty(ListItemUrl))
                {
                    SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                        #region GetListitems by Url
                        using (SPSite currentsite = new SPSite(SPContext.Current.Site.ID))
                        {
                            string FullListItemUrl = SPUrlUtility.CombineUrl(currentsite.RootWeb.Url, ListItemUrl);
                            try
                            {
                                using (SPSite lisite = new SPSite(FullListItemUrl))
                                {
                                    using (SPWeb liweb = lisite.OpenWeb())
                                    {
                                        listitem = liweb.GetListItem(ListItemUrl);
                                    }
                                };
                            }
                            catch (Exception ex)
                            {
                                HandleError(string.Format("Error when retrieving ListItem from Url '{0}' ({1})", ListItemUrl, FullListItemUrl), ex);
                            }
                        }
                    });

                    #endregion
                }
                else
                {
                    listitem = SPContext.Current.ListItem;
                }

                // Run the code with Elevated Privileges to ensure correct functioning
                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    CreateOutputControl(listitem);
                });

                // Check if the XslTransform generated content
                if (this._partContent == null)
                {
                    // Do nothing
                    return;
                }

                // Check if the XslTransform generated content contains ServerSideControls
                if (!this.RunatChecker.IsMatch(this._partContent))
                { // No
                    // Add content as literal
                    Literal literal = new Literal();
                    literal.Text = this._partContent;
                    this.AddParsedSubObject(literal);

                    // return
                    return;
                }

                // Add static assembly references to the output
                if ((this._assemblyReferences != null) && (this._partContent != null))
                {
                    StringBuilder builder = new StringBuilder();
                    for (int i = 0; i < this._assemblyReferences.Length; i++)
                    {
                        builder.Append(this._assemblyReferences[i]);
                    }
                    builder.Append(this._partContent);
                    this._partContent = builder.ToString();
                }

                Control control = this.Page.ParseControl(this._partContent);
                this.AddParsedSubObject(control);
            }
        }

        /// <summary>
        /// An overridden implementation
        /// </summary>
        /// <returns>The standard EditorParts including the custom EditorParts (specific for this webpart)</returns>
        public override EditorPartCollection CreateEditorParts()
        {
            #region Create the editorpart for the ListItemUrl-parameter
            AssetUrlSelectorEditorPart lfwbptp = new AssetUrlSelectorEditorPart("ListItemUrl", "Select listitem to display");
            lfwbptp.Description = "A relative Url referencing the listitem which data needs to be displayed";
            lfwbptp.AssetPickerButtonVisible = true;
            lfwbptp.UseImageAssetPicker = false;
            lfwbptp.AllowExternalUrls = false;
            lfwbptp.ID = this.ID + "_ListItemUrl";
            List<EditorPart> parts = new List<EditorPart>();
            parts.Add(lfwbptp);
            #endregion
            
            #region Create the editorpart for the XSLTLink-parameter
            lfwbptp = new AssetUrlSelectorEditorPart("XSLTLink", "Select XSLT for rendering");
            lfwbptp.Description = "A relative Url referencing the XSLT to be used for rendering";
            lfwbptp.AssetPickerButtonVisible = true;
            lfwbptp.UseImageAssetPicker = false;
            lfwbptp.AllowExternalUrls = false;
            lfwbptp.ID = this.ID + "_XSLTLink";
            parts.Add(lfwbptp);
            #endregion

            parts.Add(new RenderTemplateEditorPart());

            return new EditorPartCollection(base.CreateEditorParts(), parts);
        }

    }
}
